<!DOCTYPE html>
<html lang="zh-CN">

<!-- Head tag -->
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  
  <meta name="baidu-site-verification" content="undefined" />
  <meta name="google-site-verification" content="undefined" />
  <meta name="msvalidate.01" content="undefined" />
  <meta name="360-site-verification" content="undefined" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- if page has tags, then add tags to keyword -->
  
  
  <meta name="keywords" content=",Golang,Golang基础,">
  <!-- page.description has higher priority -->
  <meta name="description" content="在修改切片时遇到的问题带来的思考">
  <link rel="shortcut icon" href="/blog/img/logo.webp">
  <title>
    
    从切片修改引发的go参数传递思考 | Evrn
    
  </title>

  <link rel="canonical" href="/blog/post/go-method-params.html">
  
<link rel="stylesheet" href="/blog/css/reset.css">

  
<link rel="stylesheet" href="/blog/css/layout.css">

  
<link rel="stylesheet" href="/blog/css/markdown.css">

  <!-- Pygments Highlight CSS -->
  
<link rel="stylesheet" href="/blog/css/highlight.css">

  
<link rel="stylesheet" href="/blog/css/geektutu.css">

  <!-- global function -->
  <script>
    window.globalAddScript = function (url, onload, onerror) {
      var s = document.createElement('script');
      s.src = url;
      onload && (s.onload = onload);
      onerror && (s.onerror = onerror);
      document.body.appendChild(s);
    }
    window.globalAddCss = function (url) {
      var s = document.createElement('link');
      s.rel = 'stylesheet';
      s.href = url;
      document.body.appendChild(s);
    }
    window.getPosition = function (ele) {
      var x = 0, y = 0;
      while (ele) {
        x += (ele.offsetLeft - ele.scrollLeft + ele.clientLeft);
        y += (ele.offsetTop - ele.scrollTop + ele.clientTop);
        ele = ele.offsetParent;
      }
      return { x: x, y: y };
    }
    window.getDom = function (str) { return document.querySelector(str) }
  </script>
  <!-- google ad -->
  
<meta name="generator" content="Hexo 4.2.0"></head>

<body>
    <header class="gkt-header col-xs-12 padding-0">
    <div id="gkt-nav" class="gkt-header-container">
        <a href="/blog/" class="gkt-header-title float-left">
            <img class="float-left" src="/blog/img/logo.webp" alt="">
            <span>Evrn</span>
        </a>
        <nav class="gkt-header-nav text-right">
            <ul>
                <li><a class="hidden-xs" href="/blog/">主页</a></li>
                <li><a href="/blog/series/">专题</a></li>
                <li><a href="/blog/archives/">归档</a></li>
                <li><a href="/blog/post/link.html">友链</a></li>
                <li><a href="/blog/post/about.html">留言</a></li>
            </ul>
        </nav>
    </div>
    <div id="gkt-cate-nav" class="gkt-header-container hidden-xs">
        
        <nav class="gkt-header-nav float-left">
            <ul>
                
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-learn-interface.html">Golang学习</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-asm-indroduce.html">Golang汇编</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/tcp-three-way-handshake.html">基础</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-how-to-build.html">Golang底层</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/mysql-index-use.html">MySQL</a>
                    
                </li>
                
            </ul>
        </nav>
    </div>
</header>
<div style="height: 44px"></div>
<div class="hidden-xs" style="height: 44px"></div>
<script>
    (function () {
        window.addEventListener('scroll', function () {
            if (window.innerWidth < 768) {
                return;
            }
            var nav = document.querySelector('#gkt-nav');
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            scrollTop > 50 && (nav.classList.add('hide'));
            scrollTop <= 50 && (nav.classList.remove('hide'));
        });
        var cateNavs = document.querySelectorAll('#gkt-cate-nav>nav>ul>li');
        [].slice.call(cateNavs).forEach(function (item) {
            var sub = item.querySelector('.gkt-sub-cate');
            if (!sub) return;
            item.addEventListener('mouseenter', function (e) { sub.style.display = 'block'; }, false);
            item.addEventListener('mouseleave', function (e) { sub.style.display = 'none'; }, false);
        })
    })();
</script>
    <!-- Main Content -->
    <div class="main-container">
        <!-- Main Content -->
<main class="col-xs-12 padding-0 markdown-it">
    <!-- Post Container -->
    
    
    <!-- Post Content -->
<div class="float-left post-container box-shadow">
    <div class="u-arrow-wrapper hidden-xs">
        
        <a class="float-left" href="/blog/post/go-learn-interface.html"><i class="u-arrow-left"></i></a>
        
        
        <a class="float-right" href="/blog/post/go-asm-indroduce.html"><i class="u-arrow-right"></i></a>
        
    </div>
    <article class="col-xs-12">
        <h1> 从切片修改引发的go参数传递思考 </h1>

        

        
        
        
        <h2 id="Go只有值传递"><a href="#Go只有值传递" class="headerlink" title="Go只有值传递"></a>Go只有值传递</h2><p>我们都知道在Go参数只有值传递，也就是说无论你传的是值类型，还是引用类型，在传给参数的时候都是传递参数的一个副本。</p>
<p>如果是值类型直接就是一个拷贝。</p>
<p>如果是引用类型或者传指针，他也给你拷贝一份副本，不过拷贝的是<strong>指针的地址</strong>，而不是指针指向的内存的值，这样就给你一种引用传递的<strong>错觉</strong>，实际上还是值传递。</p>
<p>下面我们看个例子。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">   i := <span class="number">10</span></span><br><span class="line">   ip := &amp;i</span><br><span class="line">   fmt.Printf(<span class="string">"原始指针的内存地址是：%p\n"</span>, &amp;ip)</span><br><span class="line">   modify(ip)</span><br><span class="line">   fmt.Println(<span class="string">"int值被修改了，新值为:"</span>, i)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">modify</span><span class="params">(ip *<span class="keyword">int</span>)</span></span> &#123;</span><br><span class="line">   fmt.Printf(<span class="string">"函数里接收到的指针的内存地址是：%p\n"</span>, &amp;ip)</span><br><span class="line">   *ip = <span class="number">1</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们运行，可以看到输入结果如下：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">原始指针的内存地址是：0xc42000c028</span><br><span class="line">函数里接收到的指针的内存地址是：0xc42000c038</span><br><span class="line">int值被修改了，新值为: 1</span><br></pre></td></tr></table></figure>

<p>首先我们要知道，任何存放在内存里的东西都有自己的地址，指针也不例外，它虽然指向别的数据，但是也有存放该指针的内存。</p>
<p><img src="http://river.marvelthanos.com/blog/20200308/QNt0ul3kSbpF.png?imageslim" alt="mark"></p>
<p>虽然传递的是指针，实际上是两个不同的地址的指针，只是他们指向的是同一块内存，所以函数里面对指针副本的修改也能体现到函数外面</p>
<h2 id="map"><a href="#map" class="headerlink" title="map"></a>map</h2><p>我们知道map在make的时候编译阶段会被替换成makemap，而makemap实际上返回的是一个指针，因此map作为参数，实际上就是指针的传递</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// makemap implements a Go map creation make(map[k]v, hint)</span></span><br><span class="line"><span class="comment">// If the compiler has determined that the map or the first bucket</span></span><br><span class="line"><span class="comment">// can be created on the stack, h and/or bucket may be non-nil.</span></span><br><span class="line"><span class="comment">// If h != nil, the map can be created directly in h.</span></span><br><span class="line"><span class="comment">// If bucket != nil, bucket can be used as the first bucket.</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">makemap</span><span class="params">(t *maptype, hint <span class="keyword">int64</span>, h *hmap, bucket unsafe.Pointer)</span> *<span class="title">hmap</span></span> &#123;</span><br><span class="line">    <span class="comment">//省略无关代码</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="chan"><a href="#chan" class="headerlink" title="chan"></a>chan</h2><p>本质上和<code>map</code>类型是一样的，make会替换成makechan</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">makechan</span><span class="params">(t *chantype, size <span class="keyword">int64</span>)</span> *<span class="title">hchan</span></span> &#123;</span><br><span class="line">    <span class="comment">//省略无关代码</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="struct"><a href="#struct" class="headerlink" title="struct"></a>struct</h2><p>结构体在go里面属于<strong>值类型</strong>，也就是说在参数传递的时候传递的是整份拷贝，如果是超大对象可以考虑使用指针传递</p>
<h2 id="array"><a href="#array" class="headerlink" title="array"></a>array</h2><p>属于值类型，在参数传递的时候也是值传递，它跟切片的不同是一旦初始化就分配了确定的内存，以后赋值不需要再根据需要不断划分更多空间</p>
<h2 id="slice"><a href="#slice" class="headerlink" title="slice"></a>slice</h2><p>这个比较特殊，它既不属于值类型，也不属于引用类型，也是引起这篇文章的原因</p>
<p>先来回顾一下slice，切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用底层数组，设定<strong>相关属性（len）</strong>将数据读写操作限定在指定的区域内。<strong>切片本身是一个只读对象，其工作机制类似数组指针的一种封装</strong>。</p>
<p><img src="http://river.marvelthanos.com/blog/20200309/K3fPOpmsnDuq.png?imageslim" alt="mark"></p>
<p>切片的底层数据结构</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> slice <span class="keyword">struct</span> &#123;</span><br><span class="line">   array unsafe.Pointer  <span class="comment">//指向底层数组第一个元素的地址</span></span><br><span class="line">   <span class="built_in">len</span>   <span class="keyword">int</span>			<span class="comment">//当前切片限制读取的大小</span></span><br><span class="line">   <span class="built_in">cap</span>   <span class="keyword">int</span>			<span class="comment">//底层数组的大小</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到，不同的切片有可能是通过指针共享同一个底层数组。</p>
<p>修改切片可能会影响共享同一个数组的其他切片的值，请看例子</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">a := [<span class="number">6</span>]<span class="keyword">int</span>&#123;&#125;</span><br><span class="line">b := a[<span class="number">1</span>:<span class="number">3</span>:<span class="number">4</span>] <span class="comment">//指向1-3，新cap为4-1=3</span></span><br><span class="line">c := a[<span class="number">2</span>:<span class="number">6</span>]</span><br><span class="line">fmt.Printf(<span class="string">"before append %p\n"</span>,b)<span class="comment">//before append 0xc000080038</span></span><br><span class="line">b = <span class="built_in">append</span>(b, <span class="number">1</span>)</span><br><span class="line">fmt.Printf(<span class="string">"append 1 %p\n"</span>,b) <span class="comment">//append 1 0xc000080038 还是原数组</span></span><br><span class="line">b = <span class="built_in">append</span>(b, <span class="number">2</span>)</span><br><span class="line">fmt.Printf(<span class="string">"append 2 %p\n"</span>,b)<span class="comment">//append 2 0xc000080060 分配了新的数组</span></span><br><span class="line">fmt.Println(c)  <span class="comment">//[0 1 0 0] append 1 时c切片受了影响 append 2时已经指向不同数组</span></span><br></pre></td></tr></table></figure>

<p>可以看到，当b和c共享a数组的时候，b对数组的更改会体现到切片c上，一旦b的底层数组不满足长度重新分配数组后，b做的更改就不体现在c上</p>
<p><img src="http://river.marvelthanos.com/blog/20200309/YRUStldUW4Cb.png?imageslim" alt="mark"></p>
<p><img src="http://river.marvelthanos.com/blog/20200309/3MEz1qM6xIOd.png?imageslim" alt="mark"></p>
<h3 id="slice参数传递"><a href="#slice参数传递" class="headerlink" title="slice参数传递"></a>slice参数传递</h3><p>slice其实是一个很特别的数据结构，他本身是一个struct，struct是只传递拷贝的，函数内更改与函数外无关，但是slice的struct里面又包含一个指向底层数组的指针，函数内对于底层数组的更改又能体现到函数外面，这就造成一个很奇怪的问题。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">changeSlice</span><span class="params">(arr []<span class="keyword">int</span>)</span></span>&#123;</span><br><span class="line">	arr[<span class="number">0</span>]=<span class="number">11</span></span><br><span class="line">	arr=<span class="built_in">append</span>(arr,<span class="number">22</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	a := <span class="built_in">make</span>([]<span class="keyword">int</span>, <span class="number">1</span>, <span class="number">6</span>)</span><br><span class="line">	changeSlice(a)</span><br><span class="line">	fmt.Println(a)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>打印出来的结果是</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[11]</span><br></pre></td></tr></table></figure>

<p>函数内对切片的<strong>append操作</strong>无法体现，但是对于<strong>索引</strong>的操作又能体现。</p>
<p>这其实就跟切片的这个struct有关</p>
<p>传递参数的时候传递的是内存的一个副本，在函数里面直接改索引的值，相当于直接操作数组，因此能够体现到函数外。</p>
<p>但是append操作在插入数据到底层数组的同时，会更改slice的struct的len值len:1=&gt;len:2,但是这个len改的只是<strong>副本</strong>的struct里面的len，外面的slice struct里面的len还是1，但是在读取切片的时候又依靠这个len所以，只能读到11。</p>
<p><img src="http://river.marvelthanos.com/blog/20200309/YABQIcpMbihj.png?imageslim" alt="mark"></p>
<p>那怎么证明函数内外的切片指向同一个数组，函数内又真的把22插进去了呢？</p>
<p>我们知道数组的存储是一片连续的内存，我们可以通过对指向首元素地址的数组指针作适当的偏移，指向到新元素上面</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">bh := (*reflect.SliceHeader)(unsafe.Pointer(&amp;a)) <span class="comment">//强转为slice struct</span></span><br><span class="line">*(*<span class="keyword">int</span>)(unsafe.Pointer(bh.Data+<span class="number">8</span>)) <span class="comment">//amd64，int占8字节,打印22</span></span><br></pre></td></tr></table></figure>

<p>最终代码</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">changeSlice</span><span class="params">(arr []<span class="keyword">int</span>)</span></span>&#123;</span><br><span class="line">	arr[<span class="number">0</span>]=<span class="number">11</span></span><br><span class="line">	arr=<span class="built_in">append</span>(arr,<span class="number">22</span>)</span><br><span class="line">	fmt.Printf(<span class="string">"函数内 底层数组地址 %p \n"</span>, arr)</span><br><span class="line">	fmt.Printf(<span class="string">"函数内 变量地址 %x \n"</span>,unsafe.Pointer(&amp;arr))</span><br><span class="line">	fmt.Printf(<span class="string">"函数内 切片内容 %v \n"</span>, arr)</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	a := <span class="built_in">make</span>([]<span class="keyword">int</span>, <span class="number">1</span>, <span class="number">10</span>)</span><br><span class="line">	fmt.Printf(<span class="string">"函数外 底层地址 %p \n"</span>, a)</span><br><span class="line">	fmt.Printf(<span class="string">"函数外 变量地址 %x \n"</span>, unsafe.Pointer(&amp;a))</span><br><span class="line">	changeSlice(a)</span><br><span class="line">	fmt.Printf(<span class="string">"函数外 切片a内容 %v \n"</span>, a)</span><br><span class="line">	bh := (*reflect.SliceHeader)(unsafe.Pointer(&amp;a)) <span class="comment">//强转为slice struct</span></span><br><span class="line">	fmt.Printf(<span class="string">"a[1]:%v"</span>,*(*<span class="keyword">int</span>)(unsafe.Pointer(bh.Data+<span class="number">8</span>))) <span class="comment">//amd64，int占8字节</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最终结果</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">函数外 底层地址 0xc000090000 </span><br><span class="line">函数外 变量地址 c000054400 </span><br><span class="line">函数内 底层数组地址 0xc000090000 </span><br><span class="line">函数内 变量地址 c000054460 </span><br><span class="line">函数内 切片内容 [11 22] </span><br><span class="line">函数外 切片a内容 [11] </span><br><span class="line">a[1]:22</span><br></pre></td></tr></table></figure>

<h2 id="fmt-print对slice指针打印的特殊处理"><a href="#fmt-print对slice指针打印的特殊处理" class="headerlink" title="fmt.print对slice指针打印的特殊处理"></a>fmt.print对slice指针打印的特殊处理</h2><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在上面我对slice变量的地址打印使用</span></span><br><span class="line">fmt.Printf(<span class="string">"变量地址 %x \n"</span>, unsafe.Pointer(&amp;a))</span><br><span class="line"><span class="comment">//为什么不直接使用%p打印</span></span><br><span class="line">fmt.Printf(<span class="string">"变量地址 %p \n"</span>,a)</span><br></pre></td></tr></table></figure>

<p>在fmt.print的源码我们看到，对于指针的打印有特殊的处理</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *pp)</span> <span class="title">fmtPointer</span><span class="params">(value reflect.Value, verb <span class="keyword">rune</span>)</span></span> &#123;</span><br><span class="line">   <span class="keyword">var</span> u <span class="keyword">uintptr</span></span><br><span class="line">   <span class="keyword">switch</span> value.Kind() &#123;</span><br><span class="line">   <span class="keyword">case</span> reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:</span><br><span class="line">      u = value.Pointer()</span><br><span class="line">   <span class="keyword">default</span>:</span><br><span class="line">      p.badVerb(verb)</span><br><span class="line">      <span class="keyword">return</span></span><br><span class="line">   &#125;</span><br><span class="line">......</span><br></pre></td></tr></table></figure>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v Value)</span> <span class="title">Pointer</span><span class="params">()</span> <span class="title">uintptr</span></span> &#123;</span><br><span class="line">   <span class="comment">// <span class="doctag">TODO:</span> deprecate</span></span><br><span class="line">   k := v.kind()</span><br><span class="line">   <span class="keyword">switch</span> k &#123;</span><br><span class="line">   <span class="keyword">case</span> Chan, Map, Ptr, UnsafePointer:</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">uintptr</span>(v.pointer())</span><br><span class="line">......</span><br><span class="line">   <span class="keyword">case</span> Slice:</span><br><span class="line">      <span class="keyword">return</span> (*SliceHeader)(v.ptr).Data</span><br><span class="line">   &#125;</span><br><span class="line">......</span><br></pre></td></tr></table></figure>

<p>对于 Chan, Map, Ptr, UnsafePointer直接打印指针的值，但是对于slice，其实是先强制转换为SliceHeader，然后再取里面<strong>底层指针的地址</strong>，这样我们是无法得到真实的变量地址</p>
<p>效果类似于</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">bh := (*reflect.SliceHeader)(unsafe.Pointer(&amp;arr))<span class="comment">//强转切片</span></span><br><span class="line">fmt.Printf(<span class="string">" %x \n"</span>, bh.Data)<span class="comment">//获取底层数组首元素地址</span></span><br></pre></td></tr></table></figure>



<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>go里面只有值传递，无论是指针还是值类型，都是传递一个副本，一个拷贝</p>
<p>切片slice不是完全意义上的传指针，要注意对len，cap这些值类型的变化</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://www.flysnow.org/2018/02/24/golang-function-parameters-passed-by-value.html" target="_blank" rel="noopener">Go语言参数传递是传值还是传引用</a></p>
<p><a href="https://www.jianshu.com/p/030aba2bff41" target="_blank" rel="noopener">深入解析Go中 Slice 底层实现</a></p>
<p><a href="https://www.cnblogs.com/vinsent/p/11326417.html" target="_blank" rel="noopener">Golang理解-数组和切片</a></p>

        
<script src="/blog/js/qrious.min.js"></script>


        <hr>
        <div>
            
            <p>
                <span>本文发表于 2019-04-19，最后修改于 2020-03-10。</span>
            </p>
            <!-- 文章末尾的提示 start -->
            
            
            
                
            <!-- 文章末尾的提示 end -->
        </div>
        <hr />
        <p name="pagination" style="font-size: 1.2em">
    
    <a class="float-left" href="/blog/post/go-learn-interface.html">上一篇 « Go学习 接口和类型断言</a>
    
    
    <a class="float-right" href="/blog/post/go-asm-indroduce.html">下一篇 » Golang汇编(一)-Plan9介绍及指令详解</a>
    
</p>
    </article>
    <!-- 赞赏 -->
    <!--打赏-->

    <!-- 推荐阅读三篇文章 -->
    <div class="col-xs-12">
        <h3>推荐阅读</h3>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/go.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang底层"
                    title="Golang底层">Golang底层</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang"
                    title="Golang">Golang</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/go-how-to-build.html" class="title">
                Golang程序编译执行流程
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-03-01，</span>
                    <span class="hidden-xs">全文7487字，</span>
                    <span>阅读约25分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/go.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang"
                    title="Golang">Golang</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang基础"
                    title="Golang基础">Golang基础</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/go-method-params.html" class="title">
                从切片修改引发的go参数传递思考
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2019-04-19，</span>
                    <span class="hidden-xs">全文5452字，</span>
                    <span>阅读约19分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/related_links/relatelink.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#友链"
                    title="友链">友链</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/link.html" class="title">
                友链
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2011-02-03，</span>
                    <span class="hidden-xs">全文241字，</span>
                    <span>阅读约1分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
    </div>
    <div class="col-xs-12">
        <!-- 标签列表 -->
        <!-- Featured Tags -->
<style>
    #featured-tag .post-tag-item {
        font-size: 12px;
        line-height: 30px;
        display: inline-block;
        height: 30px;
        margin: 5px 0px;
        padding: 0 7px;
        color: #333;
        border-radius: 15px;
        background: #f6f6f6;
    }

    #featured-tag .post-tag-item:hover {
        color: #337ab7;
    }
</style>
<div id="featured-tag">
    
    <a class="post-tag-item" href="/blog/tags/#关于我" title="关于我"
        rel="1">#关于我 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#友链" title="友链"
        rel="1">#友链 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang汇编" title="Golang汇编"
        rel="4">#Golang汇编 (4) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang底层" title="Golang底层"
        rel="5">#Golang底层 (5) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang" title="Golang"
        rel="8">#Golang (8) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang基础" title="Golang基础"
        rel="1">#Golang基础 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang源码" title="Golang源码"
        rel="2">#Golang源码 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#基础" title="基础"
        rel="2">#基础 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#tcp/http" title="tcp/http"
        rel="2">#tcp/http (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL" title="MySQL"
        rel="3">#MySQL (3) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL优化" title="MySQL优化"
        rel="1">#MySQL优化 (1) </a>
    
</div>
    </div>
    <!-- 评论 -->
    <div class="col-xs-12">
        
    </div>
</div>
<aside class="float-left gkt-sidebar hidden-xs hidden-sm">
    <div style="clear: both"></div>
    <div class="gkt-sidebar-wrapper">
        <section class="box-shadow"><style>
    .gkt-summary {
        border: 1px solid #DDDDDD;
        border-radius: 3px;
        padding: 5px;
        width: 100%;
    }


    .gkt-summary nav {
        overflow: hidden;
    }

    .gkt-summary nav a {
        display: inline-block;
        text-align: center;
        color: #333;
        font-size: 12px;
    }

    .gkt-summary nav span {
        display: block;
    }

    .gkt-summary nav .middle {
        border-left: 1px solid #eaecef;
        border-right: 1px solid #eaecef;
    }

    .gkt-summary .number {
        font-weight: bold;
    }

    .gkt-summary .link-list {
        margin-top: 5px;
        margin-bottom: -5px;
        padding-top: 7px;
        border-top: 1px dashed #999;
        display: flex;
    }

    .gkt-summary .link-list a {
        flex: 1;
    }

    .gkt-summary .link-list img {
        width: 25px;
        height: 25px;
    }
</style>

<div class="gkt-summary">
    <nav>
        <a href="/blog/" class="col-xs-4">
            <number class="number">15</number><span>文章</span>
        </a>
        <a href="/blog/series" class="col-xs-4 middle">
            <number class="number">5</number><span>专题</span>
        </a>
        <a href="/blog/tags" class="col-xs-4">
            <number class="number">11</number><span>标签</span>
        </a>
    </nav>

    
    <div class="link-list">
        
        <a href="https://marvelthanos.com" target="_blank" rel="noopener"><img src="/blog/img/related_links/thanos.webp" /></a>
        
    </div>
    
</div></section>
        
        
        
        <section class="box-shadow">
            <strong>最近的文章</strong>
            <ul>
                
                <li>
                    <a href="/blog/post/go-learn-lib-sorts.html">Go源码学习 sort包</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-optimize-explain.html">EXPLAIN-执行计划查询命令</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-index-base.html">《高性能MySQL》笔记-索引基础</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-index-use.html">《高性能MySQL》笔记-索引优化</a>
                </li>
                
                <li>
                    <a href="/blog/post/go-how-to-build.html">Golang程序编译执行流程</a>
                </li>
                
                <li>
                    <a href="/blog/post/tcp-what-is-https.html">简述HTTPS/SSL/TLS运行过程</a>
                </li>
                
                <li>
                    <a href="/blog/post/tcp-three-way-handshake.html">简述TCP“3次握手，4次挥手”</a>
                </li>
                
                <li>
                    <a href="/blog/post/go-asm-datastruct.html">Golang汇编(四)-汇编眼中的数据结构</a>
                </li>
                
                <li>
                    <a href="/blog/post/go-asm-my-asm.html">Golang汇编(三)-编写自己的汇编代码</a>
                </li>
                
                <li>
                    <a href="/blog/post/go-asm-method.html">Golang汇编(二)-函数调用</a>
                </li>
                
            </ul>
        </section>
        <section class="box-shadow text-center">
            
        </section>
    </div>
</aside>

<script>
    (function () {
        var ele = getDom('.gkt-sidebar-content')
        var wrapper = getDom('.gkt-sidebar-wrapper')
        var last = 0
        window.addEventListener("scroll", function (e) {
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            var isDown = scrollTop > last;
            var pos = getPosition(ele).y - scrollTop;
            var downLimit = 50;
            var upLimit = -100;
            // uarrow.style.marginTop = scrollTop + 'px';
            isDown && pos <= downLimit && wrapper.classList.add("gkt-sidebar-fixed");
            !isDown && pos > upLimit && wrapper.classList.remove("gkt-sidebar-fixed");
            last = scrollTop
        })
    })();
    (function () {
        function resizeUArrow() {
            var s = getDom('.u-arrow-wrapper').style
            var pc = getDom('.post-container')
            s.left = getPosition(pc).x + 'px';
            s.width = pc.clientWidth + 'px';
        }
        resizeUArrow()
        window.addEventListener('resize', resizeUArrow);
    })();
</script>

    
</main>
    </div>
    <style>
    img#go-top {
        position: fixed;
        bottom: 100px;
        width: 50px;
        cursor: pointer;
        z-index: 9999;
    }
</style>
<img id="go-top" src="/blog/icon/top.png" class="hidden-xs" style="display: none" />
<script>
    (function () {
        var goTop = document.getElementById('go-top');
        var mainContainer = document.querySelector('.main-container');
        
        goTop.addEventListener('click', function () {
            window.scroll(0, 0);
        }, false);
        window.addEventListener('scroll', function () {
            var right = document.body.offsetWidth - mainContainer.getBoundingClientRect().right;
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            goTop.style.right = right + 10 + 'px'
            scrollTop > 700 && (goTop.style.display = "block");
            scrollTop <= 700 && (goTop.style.display = "none");
        });
    })();
</script>
    <style>
    #geektutu-click-img-container {
        position: fixed;
        left: 0;
        top: 0;
        text-align: center;
        width: 100%;
        display: none;
        z-index: 9999;
    }

    #geektutu-click-img-container img {
        object-fit: contain;
        background: #eaecef;
        padding: 15px;
        border-radius: 10px;
        height: auto;
        width: auto;
        vertical-align: middle;
    }
</style>


<div id="geektutu-click-img-container">
    <img src="" alt="Big Image">
</div>

<script>
    (function () {
        var container = document.querySelector('#geektutu-click-img-container')
        var targetImg = container.querySelector('img')
        var imgs = document.querySelectorAll('article img');
        targetImg.addEventListener('click', function (e) {
            container.style.display = 'none';
            e && e.stopPropagation && e.stopPropagation();
        }, false);

        for (var i = 0; i < imgs.length; ++i) {
            var img = imgs[i];
            img.addEventListener('click', (function (src, rate) {
                return function (e) {
                    e && e.stopPropagation && e.stopPropagation();
                    if (window.innerWidth < 980) {
                        return
                    }
                    targetImg.style.height = targetImg.style.width = 'auto';
                    if (window.innerWidth / window.innerHeight > rate) {
                        targetImg.style.height = (window.innerHeight - 20) + 'px';
                    } else {
                        targetImg.style.width = (window.innerWidth - 20) + 'px';
                    }
                    container.style.height = window.innerHeight + 'px'
                    container.style.lineHeight = window.innerHeight + 'px'
                    container.style.display = 'block';
                    targetImg.src = src;
                };
            }(img.src, img.width / img.height)), false)
        }
    })();
</script>
    <!-- Footer -->
    <!-- Footer -->
<style>
    footer {
        width: 100%;
        line-height: 1.5;
        padding: 20px;
    }

    footer a {
        color: #333;
        text-decoration: none;
    }

    .footer-hexo img {
        height: 20px;
        margin-bottom: -5px;
    }

    .footer-hexo a {
        color: #337ab7;
    }
</style>
<footer class="text-center col-xs-12">
    <p>
        <small>© 2020 - Evrn - </small>
        <small>
            <a target="_blank" rel="nofollow noopener" href="http://www.miitbeian.gov.cn/"></a>
        </small>
    </p>
    <p class="footer-hexo">
        <!-- 但若直接使用或修改主题，请务必保留这段声明 -->
        <small>Powered by <a target="_blank" href="https://hexo.io">Hexo</a> | Theme
            <a target="_blank" href="https://geektutu.com">Geektutu</a>
            <a href="https://github.com/geektutu/hexo-theme-geektutu" target="_blank" rel="noopener">
                <img src=""
                    alt="Github Star">
            </a>
        </small>
    </p>
    
    <p>
        <small>
            <span id="busuanzi_container_site_pv">👁<span id="busuanzi_value_site_pv"></span></span> &nbsp;
            <span id="busuanzi_container_page_pv">📚<span id="busuanzi_value_page_pv"></span></span>
        </small>
    </p>
    
    

</footer>


<script>
    window.addEventListener('load', function () {
        globalAddScript('//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js');
    })
</script>

<!-- Baidu Tongji -->





    <script>
        // 非本站网址，新开tab页签
        (function () {
            var stopBubble = function (e) {
                e && e.stopPropagation && e.stopPropagation();
            }
            var links = document.querySelectorAll('a');
            [].slice.call(links).forEach(function (item) {
                if (item.href && item.href.indexOf(window.location.host) === -1) {
                    item.target = '_blank'
                }
                // 阻止冒泡，不触发彩蛋。
                item.addEventListener('click', stopBubble, false);
            });
            var article = document.querySelector('article');
            article && article.addEventListener('click', stopBubble, false)
        })();
    </script>
    
</body>

</html>